package bsts.view.util;

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
/*
 * Author 		: Lee Zhong De Rollei
 * Student ID	: A0079953L
 */
public class GridControl extends JPanel implements ActionListener{

	/*Local attributes=================================================================================================*/
	//required as it references serializable objects for binary saving
	private static final long serialVersionUID = 1L;
	//Grid variables
	private Object objSource;
	private String objSourceMethod;
	private ArrayList<GridColumn> gridCol;
	private ArrayList<?> objList;
	private boolean showFilter;
	private MouseListener mse_list;
	private String[] columnNames;
	private String[][] dataValues;
	
	// UI variables
	private	JTable		grid;
	private	JScrollPane scrollPane;
	
	//UI variables Used for Filter Panel
	private JPanel filterPanel;
	private JComboBox cBox;
	private GridColumn colObj;
	private JTextField tf;
	private JButton btnSearch;

	/*Constructors=======================================================================================================*/
	public GridControl(int height, int width,Object objSource,String objSourceMethod, ArrayList<GridColumn> gridCol,boolean showFilter, MouseListener mse_list)
	{ 
		setSize(height,width);		
		this.gridCol = gridCol;
		this.showFilter =showFilter; 
		this.mse_list = mse_list;
		this.objSource = objSource;
		this.objSourceMethod = objSourceMethod;		
		setDataSource(objSource, objSourceMethod);
		initializeFilterPanel();
		initializeGrid();		
	}

	public GridControl(int height, int width,Object objSource,String objSourceMethod, ArrayList<GridColumn> gridCol,boolean showFilter)
	{ 
		setSize(height,width);		
		this.gridCol = gridCol;
		this.showFilter =showFilter; 
		this.objSource = objSource;
		this.objSourceMethod = objSourceMethod;		
		setDataSource(objSource, objSourceMethod);
		initializeFilterPanel();
		initializeGrid();		
	}
	

	/*Methods to initialize GridControl =================================================================================*/	
	private void initializeGrid(){				
	   //declare local variables***********************************************************
	   String recFoundMsg;	   
	   
	   //define column used to bind JTable based on gridColumn collection******************
	   columnNames = new String[gridCol.size()];		
	    for(int i=0;i<gridCol.size();i++){
	    	GridColumn colObj = gridCol.get(i);
			columnNames[i]=colObj.getColDisplayName();
	    }
		
	   //define data value used to bind JTable based on object collection******************
		dataValues = new String[objList.size()][gridCol.size()];
		for(int i=0;i<objList.size();i++){
			Object obj = objList.get(i);		
			dataValues[i] = GetRowData(obj);
		}

		//initialize JTable*****************************************************************
		initializeJTable();
		
		//define records found message******************************************************
		recFoundMsg = " Displaying "+String.valueOf(objList.size())+" out of "+String.valueOf(objList.size())+" record(s) Found !";
		
		//Create GridControl Panel**********************************************************
		setLayout(new BorderLayout());
		this.removeAll();
		this.add(filterPanel,BorderLayout.NORTH);
		this.add(scrollPane,BorderLayout.CENTER);	
		this.add(new JLabel(recFoundMsg),BorderLayout.SOUTH);
		this.repaint();
		this.validate();
	}

	private void initializeJTable(){
		//Create Model with uneditable cell as the table is only for viewing
		TableModel model = new DefaultTableModel(dataValues, columnNames){
			private static final long serialVersionUID = 1281697969128810817L;

		public boolean isCellEditable(int rowIndex, int colIndex) {
	          return false;
	        }
	      };
		
	    //set model properties to allow sorting
	    RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);
		grid = new JTable(model);
		grid.setToolTipText("Double Click to Select The Item");
		grid.addMouseListener(this.mse_list);		
		grid.setRowSorter(sorter);
        
		//Set column width Width		
		for(int i=0;i<gridCol.size();i++){
			colObj = gridCol.get(i);
			TableColumn column = grid.getColumnModel().getColumn(i);
			column.setPreferredWidth(colObj.getColWidth());			
		}
		
		//add JTable to scrolling panel
		scrollPane = new JScrollPane(grid);
	}
	
	private void initializeFilterPanel(){
	 	if (this.showFilter == false){
	 		//no filtering required hence return empty panel
	 		filterPanel = new JPanel();
	 	}
	 	//Instantiate local variables	 	
		cBox = new JComboBox();
		tf = new JTextField();
		btnSearch = new JButton("Search");			
		btnSearch.addActionListener(this);
		btnSearch.setActionCommand("filter");
		
		//Create controls used in filter panel****************************
		//Create ComboBox based on the column		
		for(int i=0;i<gridCol.size();i++){
			colObj = gridCol.get(i);
			cBox.addItem(colObj.getColDisplayName());
		}
		//Set Controls Size
		cBox.setPreferredSize(new Dimension(200,25));
		tf.setPreferredSize(new Dimension(200,26));
		btnSearch.setPreferredSize(new Dimension(80,25));
		
		//create the filter panel*****************************************
		filterPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		filterPanel.add(cBox);
		filterPanel.add(tf);
		filterPanel.add(btnSearch);					
		filterPanel.setPreferredSize(new Dimension(400,65));
		filterPanel.setBorder(BorderFactory.createTitledBorder("Filter"));	 
	}
	
	//Based on the columns and object collection. Reflect the object to get the object value
	private String[] GetRowData(Object object) {
		String[] obj = new String[this.gridCol.size()];
		String colName;
		for(int i=0; i<obj.length;i++){
			colName = gridCol.get(i).getColObjName();
			try {
				obj[i] = GetFieldValue(object,colName);
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 			
		}
		return obj;
	}

	//make use of reflection to invoke object get method to get object attribute value 
	private String GetFieldValue(Object obj, String fieldName){	
        Method meth;
        Object retobj = new Object();
		try {
			meth = obj.getClass().getMethod(fieldName);
			retobj = meth.invoke(obj);
		} catch (NoSuchMethodException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
         
         String retval = String.valueOf(retobj);
         return retval;
	}
	
	//to get the method name of the object to invoke
	private String getColObjName(String colDisplayName){
		GridColumn colObj;
		for(int i=0;i<gridCol.size();i++){
			colObj = gridCol.get(i);
			if(colObj.getColDisplayName().toUpperCase().equals(colDisplayName.toUpperCase())){
				return colObj.getColObjName();
			}			
		}
		return "";
	}
	
	//to filter the object based on user's filtering criteria
	private void filterObjList(String filterField, String filterValue){

		//start to filter based on refreshed
		ArrayList<Object> filteredObjList = new ArrayList<Object>();		
		for(int i=0;i<objList.size();i++){
			Object obj = objList.get(i);
			String objFieldValue = GetFieldValue(obj,filterField);
			if(objFieldValue.toUpperCase().indexOf(filterValue.toUpperCase())>-1){
				filteredObjList.add(obj);
			}
		}
		this.objList = filteredObjList;
		
	}
	
	/*Public methods for CALLER action listener to determine which rows and cols been clicked ==========================*/
	public JTable getGrid() {
		return grid;
	}

	//Get datasource based on the object controller passed in
	public void setDataSource(Object obj, String method){
		Method meth;
		try {
			meth = obj.getClass().getMethod(method);
			objList = (ArrayList<?>)meth.invoke(obj);
		} catch (NoSuchMethodException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 	   		
	}
	
	//to handle filtering action
	public void actionPerformed(ActionEvent evt){
	    if(evt.getActionCommand().equals("filter")){
	    	refreshGrid();	    		
			}
	}
	
	public void refreshGrid(){
		//this method will always be called to ensure that the source is always updated, even when its updated outside this frame. 
		setDataSource(this.objSource,this.objSourceMethod);
 		String filterField = this.cBox.getSelectedItem().toString(); 
		String filterValue = this.tf.getText().replace(" ", "");
		if (filterValue.equals("")){
			initializeGrid();
		}else{	   
			String filterColName = getColObjName(filterField);
			filterObjList(filterColName,  filterValue);
			initializeGrid();	    	
		}		
	}
}
